# Java 中的线程

单线程编程模型

Java 程序都运行在 JVM 中,程序中如果没有主动创建线程的话,只会创建一个线程,即主线程(不代表 JVM 中只有一个线程,JVM 在启动主线程时,也会启动一些其他线程,如 GC 线程)

用户线程和守护线程

线程启动前,可以使用 setDaemon(true) 设置为守护线程,默认用户线程

  • 主要区别:是否阻止 JVM 正常退出(异常退出:例如 System.exit(int status) 退出 JVM (opens new window)
  • 只要还有用户线程在运行,JVM 不会退出;如果没有用户线程,都是守护线程,JVM 结束
  • 主线程只是一个普通的用户线程,且 main() 方法开始执行时,线程已经启动,不能再被设为守护线程

创建多线程主要有 3 个方法

  • 继承 Thread 类
  • 实现 Runnable 接口
  • 实现 Callable 接口

# Thread

Thread 是系统定义的一个线程处理类,任何子类只需要继承此类就可以获得线程处理的能力

Thread 继承了 Runnable 接口,子类继承 Thread 时,需要覆写 run() 方法,该方法将作为一个线程的「主方法」存在

代码样例
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(i);
        }
    }
}

class Test {
    public static void main(String[] args) {
        MyThread threadA = new MyThread();
        threadA.start();    // 继承自Thread的start()方法
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Runnable

Runnable 接口里同样存在 run() 方法,作为线程的主方法存在,但是缺少用于启动线程的 start() 方法

需要通过 public Thread(Runnable target) 间接启动线程

这种实现避免了继承所带来的单继承限制,同时 JDK1.8 之后,Runnable 成为了一个函数式接口,可以使用 Lambda 表达式简化代码

代码样例
@FunctionalInterface
public interface Runnable {
    public void run();
}

class MyThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(i);
        }
    }
}

class Test {
    public static void main(String[] args) {
        MyThread threadA = new MyThread();
        new Thread(threadA).start();
        // Or using Lambda
        new Thread(() -> {
            for (int i = 0; i < 50; i++) {
                System.out.println(i);
            }
        }).start();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# Callable

当前还存在一个问题,即 run() 方法没有返回值,从 JDK 1.5 开始提供的 Callable 接口可以解决

需要 FutureTask 和 Thread 协助启动线程并最终获得返回值,相关类关系如图

Callable相关类关系

代码样例
@FunctionalInterface
public interface Callable<V> {
    public V call() throws Exception;
}

class MyThread implements Callable<String> {
   
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 50; i++) {
            System.out.println(i);
        }
        return "Done";
    }
}

class Test {
    public static void main(String[] args) throws Exception {
        Callable<String> callA = new MyThread("a");
        FutureTask<String> futureA = new FutureTask<>(callA);
        new Thread(futureA).start();
        System.out.println("Results= " + futureA.get());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# run() 和 start() 方法

  • run():如果直接执行,则只是作为一个普通方法运行,无法创建新的线程
  • start():创建新的线程,并将线程置于就绪态,等到获取 CPU 时间片,就开始执行 run() 方法

start() 方法里调用了 start0() 方法,该方法没有方法体,但是使用了 native 的关键字修饰,表示此操作将交由底层实现(JVM 实现,JVM 负责匹配不同 OS 的底层函数)



 



 















public synchronized void start() {
   if (threadStatus != 0)
       throw new IllegalThreadStateException();
   group.add(this);
   boolean started = false;
   try {
       start0();
       started = true;
   } finally {
       try {
           if (!started) {
               group.threadStartFailed(this);
           }
       } catch (Throwable ignore) {
           /* do nothing. If start0 threw a Throwable then
             it will be passed up the call stack */
       }
   }
}

private native void start0();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Last Updated: 8/14/2020, 3:55:28 AM